package liziy.spring.beans.support;

import liziy.spring.beans.factory.BeanFactory;
import liziy.spring.beans.factory.config.BeanDefinition;
import liziy.spring.beans.factory.config.RootBeanDefinition;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 主要实现bean定义中类的实例构建
 * @author Liziy
 * @date 2020/12/18 13:30
 **/
public abstract class AbstractBeanFactory implements BeanFactory {
    //创建以及创建好的实例对象存储的集合
    private Map<String, Object> singletionObjects = new ConcurrentHashMap<>(128);
    @Override
    public <T> T getBean(String beanName) {
        return doGetBean(beanName);
    }

    private <T> T doGetBean(String beanName) {
        //暂时不考虑循环依赖问题
        //1.根据bean名称获取对应bean定义对象,
        // 为了解决不同场景下面BeanDefinition对应类型不一致问题,需要构建一个
        //XML, Annotation,@Bean 都统一一个Bean定义对象
        //XML GenericBeanDefinition
        //Ann ScannedGenericBeanDefinition
        //@Bean ScannedGenericBeanDefinition
        //需要把不同场景下面获取的bean定义进行合并RootBeanDefinition
        RootBeanDefinition rootBeanDefinition = getMergeBeanDefinition(beanName);
        Object shareObjectInstance = null;
        //判断是否已经创建过该beanName对应的实例
        if (singletionObjects.containsKey(beanName)) {
            shareObjectInstance = singletionObjects.get(beanName);
        }
        if (shareObjectInstance == null) {

            // 判断beanDefinition作用域
            if (rootBeanDefinition.isSingleton()) {
                //单利方式创建Bean
                shareObjectInstance = createBean(rootBeanDefinition);
                singletionObjects.put(beanName, shareObjectInstance);

            } else if (rootBeanDefinition.isPrototype()) {
                //多例方式创建Bean(每次创建都是新的对象)
                shareObjectInstance = createBean(rootBeanDefinition);
            }
        }

        return (T) shareObjectInstance;
    }


    private RootBeanDefinition getMergeBeanDefinition(String beanName) {
        return new RootBeanDefinition(getBeanDefinition(beanName));
    }

    protected  Object createBean(RootBeanDefinition rootBeanDefinition) {
        return doCreateBean(rootBeanDefinition);
    }

    private Object doCreateBean(RootBeanDefinition rootBeanDefinition) {
        Class<?> beanClass = rootBeanDefinition.getBeanClass();
        // 先构建bean定义对应类的实例
        Object instance = InstantiateBean(beanClass);
        //进行依赖注入
        populatebean(instance);
        return instance;
    }

    /**
     * 完成创建好实例对应的依赖对象注入
     * @param instance
     */
    private void populatebean(Object instance) {
        //首先 找出需要进行依赖注入对象的类型和名称
        //名称 1.定义了 2.没有定义默认为属性名称
        Class<?> instanceClass = instance.getClass();
        //获取类中定义的所有定义字段
        Field[] declaredFields = instanceClass.getDeclaredFields();
        Arrays.stream(declaredFields).forEach(e->{
            //默认按照类型装配
            Class<?> type = e.getType();
            singletionObjects.forEach((key,instanceObject)-> {
                //把该类型实例注入到该对象中
                e.setAccessible(true);
                if (type.isAssignableFrom(instanceObject.getClass())) {

                    try {
                        e.set(instance, instanceObject);
                    } catch (IllegalAccessException ex) {
                        ex.printStackTrace();
                    }
                } else {
                    //按照名称装配
                    if (key == e.getName()) {
                        try {
                            e.set(instance, instanceObject);
                        } catch (IllegalAccessException ex) {
                            ex.printStackTrace();
                        }
                    }
                }
            });
        });


    }

    /**
     * 通过反射构建对应类的实例(空壳,不包含依赖对象实例)
     * @param beanClass
     * @return
     */
    private Object InstantiateBean(Class<?> beanClass) {
        Object instance = null;
        try {
            instance = beanClass.newInstance();
        } catch (Exception e) {
            System.out.println("实例化" + beanClass + ",出现异常:" + e.getMessage());
        }
        return instance;
    }

    protected abstract BeanDefinition getBeanDefinition(String beanName);

}
